import io
import os
from dataclasses import dataclass
from typing import Callable, Dict, Optional

import PySimpleGUI as sg
from PIL import Image, ImageFilter, ImageOps

# 定义预览图片的最大尺寸
MAX_PREVIEW_SIZE = (600, 400)


@dataclass
class ImageState:
    """用于跟踪图像的原始版本和处理后的版本"""
    original: Optional[Image.Image] = None  # 原始图像
    processed: Optional[Image.Image] = None  # 处理后的图像


def apply_grayscale(img: Image.Image) -> Image.Image:
    """应用灰度滤镜：将彩色图像转换为灰度图像"""
    return ImageOps.grayscale(img).convert("RGB")


def apply_blur(img: Image.Image) -> Image.Image:
    """应用模糊滤镜：使用高斯模糊使图像变得模糊"""
    return img.filter(ImageFilter.GaussianBlur(radius=2))


def apply_edge_detection(img: Image.Image) -> Image.Image:
    """应用边缘检测滤镜：突出显示图像中的边缘"""
    return img.filter(ImageFilter.FIND_EDGES).convert("RGB")


def apply_sepia(img: Image.Image) -> Image.Image:
    """应用怀旧（棕褐色）滤镜：给图像添加复古的棕褐色调"""
    grayscale = ImageOps.grayscale(img)  # 先转换为灰度图像
    sepia = Image.new("RGB", grayscale.size)  # 创建新图像
    pixels = sepia.load()  # 获取像素访问对象
    
    # 遍历每个像素，应用棕褐色调算法
    for y in range(grayscale.size[1]):
        for x in range(grayscale.size[0]):
            p = grayscale.getpixel((x, y))  # 获取灰度值
            # 计算棕褐色调的RGB值
            r = min(int(p * 0.393 + p * 0.769 + p * 0.189), 255)
            g = min(int(p * 0.349 + p * 0.686 + p * 0.168), 255)
            b = min(int(p * 0.272 + p * 0.534 + p * 0.131), 255)
            pixels[x, y] = (r, g, b)  # 设置像素颜色
    
    return sepia


# 定义可用的滤镜字典，键为滤镜名称，值为对应的处理函数
FILTERS: Dict[str, Callable[[Image.Image], Image.Image]] = {
    "原图": lambda img: img,  # 原图滤镜，直接返回原图像
    "灰度": apply_grayscale,  # 灰度滤镜
    "模糊": apply_blur,  # 模糊滤镜
    "边缘检测": apply_edge_detection,  # 边缘检测滤镜
    "怀旧": apply_sepia,  # 怀旧滤镜
}


def open_image(path: str) -> Image.Image:
    """打开图像文件并转换为RGB模式"""
    image = Image.open(path)
    return image.convert("RGB")


def image_to_data(img: Image.Image) -> bytes:
        """
        因客户要求，该部分业务逻辑已折叠
        """


def prepare_preview(img: Image.Image) -> Image.Image:
        """
        因客户要求，该部分业务逻辑已折叠
        """


def update_image_preview(window: sg.Window, img: Optional[Image.Image]) -> None:
    """更新窗口中的图像预览"""
    if img is None:
        # 如果没有图像，清空预览区域
        window["-IMAGE-"].update(data=None)
        return
    
    # 准备预览图像并更新界面
    preview = prepare_preview(img)
    window["-IMAGE-"].update(data=image_to_data(preview))


def build_layout() -> list:
    """构建GUI界面布局"""
    # 控制面板列（左侧）
    control_column = [
        [
            sg.Text("文件：", size=(6, 1)),  # 文件标签
            sg.Input(key="-FILE-", enable_events=True, readonly=True),  # 文件路径输入框
            sg.FileBrowse("打开图片", file_types=(("Image Files", "*.png;*.jpg;*.jpeg;*.bmp"),)),  # 文件浏览按钮
        ],
        [sg.HorizontalSeparator()],  # 水平分隔线
        [sg.Text("滤镜效果：")],  # 滤镜效果标签
        [
            sg.Column(
                # 创建滤镜按钮列
                [[sg.Button(name, key=f"-FILTER-{name}-", size=(10, 1))] for name in FILTERS.keys()],
                scrollable=True,  # 可滚动
                vertical_scroll_only=True,  # 仅垂直滚动
                size=(160, 180),  # 固定大小
            )
        ],
        # 保存和退出按钮
        [sg.Button("保存图片", key="-SAVE-", disabled=True), sg.Button("退出", key="-EXIT-")],
    ]

    # 预览面板列（右侧）
    preview_column = [
        [sg.Text("图片预览：")],  # 预览标签
        [
            sg.Frame(
                "",  # 框架标题（空）
                [[sg.Image(key="-IMAGE-", size=MAX_PREVIEW_SIZE)]],  # 图像预览元素
                size=(MAX_PREVIEW_SIZE[0] + 20, MAX_PREVIEW_SIZE[1] + 20),  # 框架大小
            )
        ],
    ]

    # 组合整体布局：控制列 + 垂直分隔符 + 预览列
    layout = [
        [sg.Column(control_column, element_justification="left"), sg.VSeperator(), sg.Column(preview_column)]
    ]
    return layout


def main() -> None:
    """主函数：创建GUI并处理事件循环"""
    # 设置GUI主题
    sg.theme("DarkBlue3")
    
    # 创建窗口实例
    # 参数说明：
    # - "简易图片滤镜与特效处理工具": 窗口标题
    # - build_layout(): 布局函数返回的布局结构
    # - finalize=True: 立即初始化窗口
    # - resizable=False: 窗口不可调整大小
    window = sg.Window("简易图片滤镜与特效处理工具", build_layout(), finalize=True, resizable=False)

    # 初始化图像状态
    state = ImageState()
    current_filter = "原图"  # 当前选中的滤镜

    # 事件循环：持续监听用户操作
    while True:
        event, values = window.read()  # 读取窗口事件和值
        
        # 退出条件：窗口关闭或点击退出按钮
        if event in (sg.WIN_CLOSED, "-EXIT-"):
            break

        # 文件选择事件
        if event == "-FILE-":
            path = values["-FILE-"]  # 获取文件路径
            if path and os.path.exists(path):  # 验证路径存在
                try:
                    # 打开图像并更新状态
                    state.original = open_image(path)
                    state.processed = state.original.copy()  # 创建处理副本
                    current_filter = "原图"  # 重置为原图滤镜
                    window["-SAVE-"].update(disabled=False)  # 启用保存按钮
                    update_image_preview(window, state.processed)  # 更新预览
                except (IOError, OSError) as exc:
                    # 处理图像打开错误
                    sg.popup_error(f"无法打开图片：{exc}")
                    state = ImageState()  # 重置状态
                    window["-SAVE-"].update(disabled=True)  # 禁用保存按钮
                    update_image_preview(window, None)  # 清空预览

        # 滤镜按钮点击事件
        if event.startswith("-FILTER-") and state.original is not None:
            # 从事件名中提取滤镜名称
            filter_name = event.replace("-FILTER-", "").replace("-", "")
            filter_name = filter_name or current_filter  # 处理空名称情况
            
            if filter_name in FILTERS:
                current_filter = filter_name  # 更新当前滤镜
                try:
                    # 应用选中的滤镜
                    state.processed = FILTERS[filter_name](state.original.copy())
                    update_image_preview(window, state.processed)  # 更新预览
                except Exception as exc:
                    # 处理滤镜应用错误
                    sg.popup_error(f"应用滤镜失败：{exc}")

        # 保存按钮点击事件
        if event == "-SAVE-" and state.processed is not None:
            # 弹出文件保存对话框
            save_path = sg.popup_get_file(
                "保存图片",  # 对话框标题
                save_as=True,  # 另存为模式
                file_types=(("PNG 文件", "*.png"), ("JPEG 文件", "*.jpg;*.jpeg"), ("BMP 文件", "*.bmp")),  # 文件类型
                default_extension=".png",  # 默认扩展名
            )
            if save_path:
                try:
                    # 保存处理后的图像
                    state.processed.save(save_path)
                    sg.popup_ok("保存成功！")  # 成功提示
                except (IOError, OSError) as exc:
                    # 处理保存错误
                    sg.popup_error(f"保存失败：{exc}")

    # 关闭窗口
    window.close()


if __name__ == "__main__":
    # 程序入口点，启动进入函数
    main()